<?php
use \hpnWse\nTest\stUnit as stUnitTest;

require_once (\hpnWse\fGetWseDiry() . 'hpnWse/nMvc/(1)FvMgr.php');
require_once (\hpnWse\fGetWseDiry() . 'hpnWse/nMvc/Dmdl.php');
use \hpnWse\nMvc;
use \hpnWse\nMvc\tMdl;
use \hpnWse\nMvc\tDmdl;
use \hpnWse\nMvc\tDbAcsr_Sql;

class tDrvdMdl extends tMdl
{
	public function __construct($a_Json = null, $a_Dsrlz = false)
	{
		parent::__construct($a_Json, $a_Dsrlz);

		if (! $a_Json)
		{
			$this->cLoadFromJson([
				"Laptop"=> "HP Envy 15-as110tu",
				"Price"=> 7999,
				"Bag"=> "H5M90",
				"Mouse"=> "X900"
			], true);
		}
	}
}


stUnitTest::cGrp('nMvc', 
function ()
{

//【警告】因为JS引擎对JSON.stringify的内部规则，所以务必保证属性顺序！

$g_InitJson = <<<JSON
{
"Elements": {
	"Wse_Val": [0, 1, 2, 3],
	"Wse_Tpnm":"tMdl",
	"Wse_Vldtrs": [
		{
			"c_Tpnm": "tElmtAmtRge",
			"c_Info": "元素数量不在有效范围内",
			"c_Min": 1,
			"c_Max": 10
		}
	]
},

"DrvdMdl": {
	"Wse_Val": { "Laptop": "HP Envy 15-as110tu", "Price": 7999, "Bag": "H5M90", "Mouse": "X900" },
	"Wse_Tpnm": "\\\\tDrvdMdl",
	"Wse_Optns": {
		"c_Flag": 1
	}
},

"DtbsName": "程序员们最喜欢的书",
"Presses#Id,Name": [
	{
		"Id": "301",
		"Name": "O'Reilly"
	},
	{
		"Id": "302",
		"Name": "Addison-Wesley"
	},
	{
		"Id": "303",
		"Name": "A K Peters"
	},
	{
		"Id": "304",
		"Name": "McGraw-Hill"
	}
],
"Books#Id": [
	{
		"Id": "201",
		"Name": "HTML5.The.Missing.Manual",
		"Press": "301,O'Reilly"
	},
	{
		"Id": "202",
		"Name": "CSS3.The.Missing.Manual",
		"Press": "301,O'Reilly"
	},
	{
		"Id": "203",
		"Name": "The.C++.Programming.Language.4th",
		"Press": "302,Addison-Wesley"
	},
	{
		"Id": "204",
		"Name": "Real-Time Rendering,3rd",
		"Press": "303,A K Peters"
	},
	{
		"Id": "205",
		"Name": "SQL The Complete Reference,3rd",
		"Press": "304,McGraw-Hill"
	}
],
"Likes#Id": [
	{
		"Id": "401",
		"PersonId": "101",
		"BookId": "201"
	},
	{
		"Id": "402",
		"PersonId": "101",
		"BookId": "202"
	},
	{
		"Id": "403",
		"PersonId": "102",
		"BookId": "203"
	},
	{
		"Id": "404",
		"PersonId": "102",
		"BookId": "204"
	}
],
"Persons#Id": [
	{
		"Id": "101",
		"Name": {
			"Wse_Val": "Mack",
			"Wse_Tpnm": "String",
			"Wse_Optns": {
				"c_Rqrd": 2,
				"c_Dft": "Anonymous"
			},
			"Wse_Fltrs": [{
				"c_Tpnm": "tErsWhtSpc"
			}],
			"Wse_Vldtrs": [
				{
					"c_Tpnm": "tStrLenRge",
					"c_Info": "只能包含 {{c_Min}} - {{c_Max}} 个字符！",
					"c_Min": 2,
					"c_Max": 64
				},
				{
					"c_Tpnm": "tStrPtn",
					"c_Info": "只能包含英文字母！",
					"c_Rgx": "/^[A-Za-z]+$/i"
				}
			]
		},
		"Gender": "Male",
		"Age": 29
	},
	{
		"Id": "102",
		"Name": {
			"Wse_Val": "Alice",
			"Wse_Tpnm": "String",
			"Wse_Optns": {
				"c_Rqrd": 1
			},
			"Wse_Fltrs": [{
				"c_Tpnm": "tErsWhtSpc"
			}],
			"Wse_Vldtrs": [
				{
					"c_Tpnm": "tStrLenRge",
					"c_Info": "只能包含 {{c_Min}} - {{c_Max}} 个字符！",
					"c_Min": 1,
					"c_Max": 64
				},
				{
					"c_Tpnm": "tStrPtn",
					"c_Info": "只能包含英文字母！",
					"c_Rgx": "/^[A-Za-z]+$/i"
				}
			]
		},
		"Gender": "Female",
		"Age": 26
	}
]
}
JSON;

	$l_fBase = function () use($g_InitJson)
	{
		$l_M = new tMdl(); stUnitTest::cAst(nMvc\fIsMdl($l_M), '$l_M应该是tMdl。');
		$l_Obj = 
		[
			'name'=>'Mad', 
			'books'=>
			[
				['isbn'=>123],
				['isbn'=>456],
				['isbn'=>789]
			]
		];
		stUnitTest::cAst('Mad' == nMvc\fAcsNestPpty($l_Obj, 'name'), '名字是“Mad”。');
		stUnitTest::cEq(123, nMvc\fAcsNestPpty($l_Obj, 'books[0].isbn'), 'isbn是“123”。');
		stUnitTest::cEq(456, nMvc\fAcsNestPpty($l_Obj, 'books[1].isbn'), 'isbn是“456”。');
		stUnitTest::cEq(789, nMvc\fAcsNestPpty($l_Obj, 'books[2].isbn'), 'isbn是“789”。');

		$l_DP = nMvc\fDcmpsPath(null, 'Data.Books.3.isbn'); //【注意，“[N]”将被转成“.N”！】
		stUnitTest::cEq('Data.Books.3', $l_DP['c_Mdl'], '分解后，模型路径是“Data.Books.3”，');
		stUnitTest::cEq('isbn', $l_DP['c_Fld'], '键是“isbn”。');
	};
	$l_fBase();
	
	$l_fWithJson = function () use($g_InitJson)
	{
		$l_InitJson = \hpnWse\stObjUtil::cDcdJson($g_InitJson); // 返回Array

		$l_Mdl = new nMvc\tMdl();
		$l_Mdl->cLoadFromJson($l_InitJson, true);
		$l_CnvtJson = $l_Mdl->cToJson(null, true);
		$l_Mdl->cLoadFromJson($l_CnvtJson, true);
		$l_CnvtJson2 = $l_Mdl->cToJson(null, true);

		//【这里比较l_InitJsonStr时，注意属性顺序！】
		$l_CnvtJsonStr = \hpnWse\stObjUtil::cEcdJson($l_CnvtJson);
		$l_CnvtJsonStr2 = \hpnWse\stObjUtil::cEcdJson($l_CnvtJson2);
		stUnitTest::cEq($l_CnvtJsonStr, $l_CnvtJsonStr2, 
			'序列化时，cLoadFromJson后再cToJson，JSON应该没有变化！');

		$l_CnvtJson = $l_Mdl->cToJson(null, false);
		$l_Mdl->cLoadFromJson($l_CnvtJson, false);
		$l_CnvtJson2 = $l_Mdl->cToJson(null, false);
		$l_CnvtJsonStr = \hpnWse\stObjUtil::cEcdJson($l_CnvtJson);
		$l_CnvtJsonStr2 = \hpnWse\stObjUtil::cEcdJson($l_CnvtJson2);
		stUnitTest::cEq($l_CnvtJsonStr, $l_CnvtJsonStr2, 
			'不序列化时，cLoadFromJson后再cToJson，JSON应该没有变化！');
	};
	$l_fWithJson();
	
	$l_fCrud = function () use($g_InitJson)
	{
		$l_InitJson = \hpnWse\stObjUtil::cDcdJson($g_InitJson); // 返回Array

		$l_Mdl = new nMvc\tMdl();
		//stUnitTest::cInfo(var_export($l_Mdl->cAcsRoot()->e_TrgrQue, true));
		$l_L1Mdl;
		$l_Mdl->cCrtFld("name", "anonymous");
		stUnitTest::cAst($l_Mdl->cHasFld("name"), 
			'cCrtFld("name", "anonymous")后，有键为“name”的字段。');
		stUnitTest::cEq($l_Mdl->cReadFld("name"), "anonymous", 
			'cReadFld("name")，返回"anonymous"。');
		$l_Mdl->cUpdFld("name", "Mijia");
		stUnitTest::cEq($l_Mdl->cReadFld("name"), "Mijia", 
			'cUpdFld("name", "Mijia").cReadFld("name")，返回"Mijia"。');
		$l_Mdl->cDltFld("name");
		stUnitTest::cAst(! $l_Mdl->cHasFld("name"), 
			'cDltFld("name")后，没有键为“name”的字段。');
		$l_Mdl->cCrtFld("dom", [ 'Wse_Val'=> null, 'Wse_Tpnm'=> "Resource" ]);

		$l_PhpObj = (new stdClass());
		$l_PhpObjs = [new stdClass(), new stdClass(), new stdClass()];
		stUnitTest::cAst($l_Mdl->cHasFld("dom"), 
			'cCrtFld("dom", { Wse_Val: null, Wse_Tpnm: Resource })后，有键为“dom”的字段。');
		$l_Mdl->cUpdFld("dom", $l_PhpObj, false); // 一切内建对象都是资源
		stUnitTest::cEq($l_PhpObj, $l_Mdl->cReadFld("dom"), 
			'cUpdFld("dom", $l_PhpObj, false)后，字段“dom”指向$l_PhpObj。');
		$l_Mdl->cCrtFld("doms", $l_PhpObjs);
		stUnitTest::cEq(count($l_PhpObjs), $l_Mdl->cReadFld("doms")->cGetFldAmt(), 
			'cCrtFld("doms", $l_PhpObjs)后，字段“doms”有三个字段，分别是：');
		stUnitTest::cEq($l_PhpObjs[0], $l_Mdl->cReadFld("doms")->cReadFld(0), 
			'[0]指向$l_PhpObjs[0]。');
		stUnitTest::cEq($l_PhpObjs[1], $l_Mdl->cReadFld("doms")->cReadFld(1), 
			'[1]指向$l_PhpObjs[1]。');
		stUnitTest::cEq($l_PhpObjs[2], $l_Mdl->cReadFld("doms")->cReadFld(2), 
			'[2]指向$l_PhpObjs[2]。');

		// 把某种字符串（如URL）当成是资源
		$l_Mdl->cCrtFld("url", [
			'Wse_Val' => "http://wseapp.top/logo.png",
			'Wse_Tpnm' => "Resource"
		]);
		stUnitTest::cEq($l_Mdl->cGetFldTpnm("url"), "Resource", "创建的“url”字段，值虽为字符串，却属于“Resource”类型，");
		$l_Mdl->cUpdFld("url", new stdClass());
		stUnitTest::cAst(($l_Mdl->cReadFld("url") instanceof stdClass), "因此，可以把它更新成一个stdClass。");
		$l_Mdl->cUpdFld("url", 123);
		stUnitTest::cEq($l_Mdl->cReadFld("url"), 123, "还可以把它更新成一个Number。");

		// cHasFld
		$l_Mdl->cLoadFromJson($l_InitJson, false);
		$l_L1Mdl = $l_Mdl->cReadFld("Books");
		stUnitTest::cAst($l_L1Mdl->cHasFld("#201"), 'cHasFld("#201")支持远程键。');
		$l_L1Mdl = $l_Mdl->cReadFld("Presses");
		stUnitTest::cAst($l_L1Mdl->cHasFld("#301,O'Reilly"), 'cHasFld("#301,O\'Reilly")支持多个远程键。');

		// cReadFlds，书名里有“the”，按ASCII排序
		$l_L1Mdl = $l_Mdl->cReadFld("Books");
		$i_Rgx_The = '/\\bthe\\b/i';
		$l_ReadFldsRst = $l_L1Mdl->cReadFlds(null, false,
			function ($a_Mdl, $a_Key, $a_Val) use($i_Rgx_The)
			{
				return \hpnWse\stRgxUtil::cTest($i_Rgx_The, $a_Val->cReadFld("Name"));
			},
			function ($a_Kvp1, $a_Kvp2)
			{
				return (strtolower($a_Kvp1['c_Val']->cReadFld("Name")) < 
					strtolower($a_Kvp2['c_Val']->cReadFld("Name"))) ? -1 : +1;
			});
		stUnitTest::cEq(count($l_ReadFldsRst), 4, 
			'cReadFlds(...)，读取书名含“the”的书，得到4个；依ASCII顺序排列是：');
		stUnitTest::cEq($l_ReadFldsRst[0]['c_Val']->cReadFld("Name"), 
			"CSS3.The.Missing.Manual", '[0] = "CSS3.The.Missing.Manual"');
		stUnitTest::cEq($l_ReadFldsRst[1]['c_Val']->cReadFld("Name"), 
			"HTML5.The.Missing.Manual", '[1] = "HTML5.The.Missing.Manual"');
		stUnitTest::cEq($l_ReadFldsRst[2]['c_Val']->cReadFld("Name"), 
			"SQL The Complete Reference,3rd", '[2] = "SQL The Complete Reference,3rd"');
		stUnitTest::cEq($l_ReadFldsRst[3]['c_Val']->cReadFld("Name"), 
			"The.C++.Programming.Language.4th", '[3] = "The.C++.Programming.Language.4th"');

		// cCrudViaPath
		$l_Mdl->cCrudViaPath("Likes.#401.Age", 32);
		stUnitTest::cEq($l_Mdl->cCrudViaPath("Likes.#401.Age"), 32, 
			'cCrudViaPath("Likes.#401.Age", 32)后再读取，返回32。');
		$l_Mdl->cCrudViaPath("Likes.#401.Age", \Wse::$i_Udfn);
		stUnitTest::cEq($l_Mdl->cCrudViaPath("Likes.#401.Age"), \Wse::$i_Udfn, 
			'cCrudViaPath("Likes.#401.Age", undefined)后再读取，返回undefined。');

		stUnitTest::cEq($l_Mdl, $l_L1Mdl->cCrudViaPath("^"), "用“^”：Books->[Root]");
		stUnitTest::cEq($l_L1Mdl, $l_L1Mdl->cCrudViaPath("^.Books"), "用“^”：Books->[Root]->Books");
		stUnitTest::cEq($l_L1Mdl, $l_Mdl->cCrudViaPath("Presses.^.Books"), "用“^”：[Root]->Presses->[Root]->Books");

		// cUpdFldsFromJson
		$l_L1Mdl->cUpdFldsFromJson([
			[
				"Id"=> "211",
				"Name"=> "HTML5.The.Missing.Manual（中文版）",
				"Press"=> "301,O'Reilly"
			],
			[
				"Id"=> "212",
				"Name"=> "CSS3.The.Missing.Manual（中文版）",
				"Press"=> "301,O'Reilly"
			],
			[
				"Id"=> "213",
				"Name"=> "The.C++.Programming.Language.4th（中文版）",
				"Press"=> "302,Addison-Wesley"
			],
			[
				"Id"=> "214",
				"Name"=> "Real-Time Rendering,3rd（中文版）",
				"Press"=> "303,A K Peters"
			],
			[
				"Id"=> "215",
				"Name"=> "SQL The Complete Reference,3rd（中文版）",
				"Press"=> "304,McGraw-Hill"
			]
		], false);
		stUnitTest::cEq($l_L1Mdl->cGetFldAmt(), 5, 
			'cUpdFldsFromJson(...)，从JSON递归更新字段，得到5个；依ID顺序排列是：');
		stUnitTest::cEq($l_L1Mdl->cReadFld(0)->cReadFld("Id"), "211", '[0] = "211"');
		stUnitTest::cEq($l_L1Mdl->cReadFld(1)->cReadFld("Id"), "212", '[1] = "212"');
		stUnitTest::cEq($l_L1Mdl->cReadFld(2)->cReadFld("Id"), "213", '[2] = "213"');
		stUnitTest::cEq($l_L1Mdl->cReadFld(3)->cReadFld("Id"), "214", '[3] = "214"');
		stUnitTest::cEq($l_L1Mdl->cReadFld(4)->cReadFld("Id"), "215", '[4] = "215"');

		// cDltFlds，cClrFldSet
		$l_Mdl->cReadFld("Likes")->cDltFlds(null);
		stUnitTest::cAst(! $l_Mdl->cReadFld("Likes")->cHasAnyFld(), 'cDltFlds(null)后没有任何字段。');
		$l_Mdl->cReadFld("Books")->cClrFldSet();
		stUnitTest::cAst(! $l_Mdl->cReadFld("Books")->cHasAnyFld(), 'cClrFldSet()后没有任何字段。');
	};
	$l_fCrud();

	$l_fSortFind = function () use($g_InitJson)
	{
		$l_InitJson = \hpnWse\stObjUtil::cDcdJson($g_InitJson); // 返回Array

		$l_Mdl = new nMvc\tMdl();
		$l_L1Mdl;
		$l_Mdl->cLoadFromJson($l_InitJson, false);
		$l_L1Mdl = $l_Mdl->cReadFld("Books");

		// 按书名排序
		$l_L1Mdl->cSortFlds(function ($a_V1, $a_V2)
		{
			return (strtolower($a_V1->cReadFld("Name")) < strtolower($a_V2->cReadFld("Name"))) ? -1 : +1;
		});
		stUnitTest::cEq($l_L1Mdl->cReadFld(0)->cReadFld("Name"), 
			"CSS3.The.Missing.Manual", '[0] = "CSS3.The.Missing.Manual"');
		stUnitTest::cEq($l_L1Mdl->cReadFld(1)->cReadFld("Name"), 
			"HTML5.The.Missing.Manual", '[1] = "HTML5.The.Missing.Manual"');
		stUnitTest::cEq($l_L1Mdl->cReadFld(2)->cReadFld("Name"), 
			"Real-Time Rendering,3rd", '[2] = "Real-Time Rendering,3rd"');
		stUnitTest::cEq($l_L1Mdl->cReadFld(3)->cReadFld("Name"), 
			"SQL The Complete Reference,3rd", '[3] = "SQL The Complete Reference,3rd"');
		stUnitTest::cEq($l_L1Mdl->cReadFld(4)->cReadFld("Name"), 
			"The.C++.Programming.Language.4th", '[4] = "The.C++.Programming.Language.4th"');

		// 查找插入索引
		$l_NewBookJson = [
			"Id"=> "206",
			"Name"=> "PHP & MySQL: The Missing Manual",
			"Press"=> "301,O'Reilly"
		];
		$l_IstIdx = $l_L1Mdl->cFindIstIdx($l_NewBookJson,
		function ($a_Ai, $a_Val)
		{
			return (strtolower($a_Ai->cReadFld("Name")) < strtolower($a_Val["Name"])) ? -1 : +1;
		});
		stUnitTest::cEq($l_IstIdx, 2, "新书“PHP & MySQL: The Missing Manual”的插入索引是2，插入后：");

		// 插入
		$l_L1Mdl->cCrtFld($l_IstIdx, $l_NewBookJson);
		stUnitTest::cEq($l_L1Mdl->cReadFld(0)->cReadFld("Name"), 
			"CSS3.The.Missing.Manual", '[0] = "CSS3.The.Missing.Manual"');
		stUnitTest::cEq($l_L1Mdl->cReadFld(1)->cReadFld("Name"), 
			"HTML5.The.Missing.Manual", '[1] = "HTML5.The.Missing.Manual"');
		stUnitTest::cEq($l_L1Mdl->cReadFld(2)->cReadFld("Name"), 
			"PHP & MySQL: The Missing Manual", '[2] = "PHP & MySQL: The Missing Manual"');
		stUnitTest::cEq($l_L1Mdl->cReadFld(3)->cReadFld("Name"), 
			"Real-Time Rendering,3rd", '[3] = "Real-Time Rendering,3rd"');
		stUnitTest::cEq($l_L1Mdl->cReadFld(4)->cReadFld("Name"), 
			"SQL The Complete Reference,3rd", '[4] = "SQL The Complete Reference,3rd"');
		stUnitTest::cEq($l_L1Mdl->cReadFld(5)->cReadFld("Name"), 
			"The.C++.Programming.Language.4th", '[5] = "The.C++.Programming.Language.4th"');
	};
	$l_fSortFind();

	$l_fSrch = function () use($g_InitJson)
	{
		$l_InitJson = \hpnWse\stObjUtil::cDcdJson($g_InitJson); // 返回Array

		$l_Mdl = new nMvc\tMdl();
		$l_L1Mdl;
		$l_Mdl->cLoadFromJson($l_InitJson, false);
		$l_L1Mdl = $l_Mdl->cReadFld("Books");

		// 书名里搜索“the”，按书名ASCII排序
		$i_Rgx_The = '/\\bthe\\b/i';
		//var_dump(\hpnWse\stRgxUtil::cTest($i_Rgx_The, 'Real-Time Rendering,3rd'));
		$l_L1Mdl->cSrchFlds(function ($a_Mdl, $a_Key, $a_Val) use($i_Rgx_The)
			{
				return \hpnWse\stRgxUtil::cTest($i_Rgx_The, $a_Val->cReadFld("Name"));
			},
			function ($a_V1, $a_V2)
			{
				return (strtolower($a_V1->cReadFld("Name")) < strtolower($a_V2->cReadFld("Name"))) ? -1 : +1;
			});
		stUnitTest::cAst(! $l_L1Mdl->cIsFldHide(0), "[0] = 展示。");
		stUnitTest::cAst(! $l_L1Mdl->cIsFldHide(1), "[1] = 展示。");
		stUnitTest::cAst(! $l_L1Mdl->cIsFldHide(2), "[2] = 展示。");
		stUnitTest::cAst(! $l_L1Mdl->cIsFldHide(3), "[3] = 展示。");
		stUnitTest::cAst($l_L1Mdl->cIsFldHide(4), "[4] = 隐藏。");

		stUnitTest::cEq($l_L1Mdl->cReadFld(0)->cReadFld("Name"), 
			"CSS3.The.Missing.Manual", '[0] = "CSS3.The.Missing.Manual"');
		stUnitTest::cEq($l_L1Mdl->cReadFld(1)->cReadFld("Name"), 
			"HTML5.The.Missing.Manual", '[1] = "HTML5.The.Missing.Manual"');
		stUnitTest::cEq($l_L1Mdl->cReadFld(2)->cReadFld("Name"), 
			"SQL The Complete Reference,3rd", '[2] = "SQL The Complete Reference,3rd"');
		stUnitTest::cEq($l_L1Mdl->cReadFld(3)->cReadFld("Name"), 
			"The.C++.Programming.Language.4th", '[3] = "The.C++.Programming.Language.4th"');
		stUnitTest::cEq($l_L1Mdl->cReadFld(4)->cReadFld("Name"), 
			"Real-Time Rendering,3rd", '[4] = "Real-Time Rendering,3rd"');
	};
	$l_fSrch();

	$l_fDbAcsr_Sql = function()
	{
		// 书的领域模型及OTM
		$l_BM = new tDmdl([
			'id' => '123',
			'title' => 'PHP Cookbook, 3rd Edition',
			'authors' => ['David Sklar', 'Adam Trachtenberg'], //【同press】
			'press' => 'O’Reilly', //【用名字，前端用编辑框，后端需要时插入】
			'public_date' => '2014-06-25',
			'categories' => [1, 6], // '程序设计'，'PHP'，【用id，前端用复选框】
			'recommend' => 5,
			'my_review' => 'PHP秘诀！我常用的参考书之一。',
		]);

		

		stUnitTest::cInfo(\hpnWse\stObjUtil::cEcdJson($l_BM->cToJson()));
	};
	$l_fDbAcsr_Sql();
});
//////////////////////////////////// OVER ////////////////////////////////////